
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#include "oledchar.h"
#include "oled.h"


u8 page = 1;
u8 column = 10;
u8 firstWrite = 1;

/* oled apis */
static ssize_t oled_writeCommand(u8 cmd)
{

    /*i2c 写时序：oleddev->device_address, 0x0->control byte, cmd->command byte */
    iic_write_reg(&oleddev, 0x0, cmd);
    return 0;
}

static ssize_t oled_writeData(u8 data) 
{
    /*i2c 写时序：oleddev->device_address, 0x40->control byte, data->data byte */
    iic_write_reg(&oleddev, 0x40, data);
    return 0;
}

static ssize_t oled_setCursor(u8 page, u8 col) 
{
    /* 设置光标位置, 列号，页号 */
    oled_writeCommand(0x0 | (col & 0xF));
    oled_writeCommand(0x10 | ((col & 0xF0) >> 4));
    oled_writeCommand(0xB0 | page);

    return 0;
}

static void oled_writeChar(char c)
{
    u8 i = 0;
    u8 p = 0;
    u8 charId = 0;
    u8 charWid = 0;

    if (128 - column < 16)
    {
        page += 2;
        column = 10;
    }

    if (c >= '0' && c <= '9')
    {
        charId = c - '0';
        charWid = 10;
        for (p = 0; p < 2; p ++)
        {
            oled_setCursor(p + page, column);
            for (i = p * charWid; i < (p + 1) * charWid; i ++)
            {
                oled_writeData(num[charId][i]);
            }
        }
        column += charWid;
    }
    else if (c >= 'A' && c <= 'Z')
    {
        charId = c - 'A';
        charWid = 16;
        for (p = 0; p < 2; p ++)
        {
            oled_setCursor(p + page, column);
            for (i = p * charWid; i < (p + 1) * charWid; i ++)
            {
                oled_writeData(up_char[charId][i]);
            }
        }
        column += charWid;
    }
    else if (c >= 'a' && c <= 'z')
    {
        charId = c - 'a';
        if (c == 'a')
        {
            charWid = 9;
        }
        else if (c == 'i' || c == 'l')
        {
            charWid = 6;
        }
        else if (c == 'j' || c == 'o' || c == 's' || c == 't' || c == 'z')
        {
            charWid = 8;
        }
        else if (c == 'm')
        {
            charWid = 15;
        }
        else if (c == 'n' || c == 'v' || c == 'y')
        {
            charWid = 12;
        }
        else if (c == 'w')
        {
            charWid = 14;
        }
        else
        {
            charWid = 10;
        }
        for (p = 0; p < 2; p ++)
        {
            oled_setCursor(p + page, column);
            for (i = p * charWid; i < (p + 1) * charWid; i ++)
            {
                oled_writeData(low_char[charId][i]);
            }
        }
        column += charWid;
    }
    else
    {
        switch(c)
        {
            case ' ':
                oled_writeData(0x0);
                oled_writeData(0x0);
                oled_writeData(0x0);
                oled_writeData(0x0);
                column += 4;
                break;
            case '.':
                oled_setCursor(page + 1, column);
                for (i = 0; i < 4; i ++)
                {
                    oled_writeData(end_point[i]);
                }
                column += 4;
                break;
            case ',':
                oled_setCursor(page + 1, column);
                oled_writeData(comma[0]);
                oled_writeData(comma[1]);
                column += 4;
                break;
            case '/':
                for (p = 0; p < 2; p ++)
                {
                    oled_setCursor(p + page, column);
                    for (i = p * 8; i < (p + 1) * 8; i ++)
                    {
                        oled_writeData(slash[i]);
                    }
                }
                column += 8;
                break;
            case ':':
                oled_setCursor(page + 1, column);
                oled_writeData(colon[0]);
                oled_writeData(colon[1]);
                oled_writeData(colon[2]);
                column += 4;
                break;
            case '~':
                column += 8;
                break;
            case 176:
                oled_setCursor(page, column);
                oled_writeData(angle_symbol[0]);
                oled_writeData(angle_symbol[1]);
                oled_writeData(angle_symbol[2]);
                oled_writeData(angle_symbol[3]);
                column += 4;
                break;
            default:
                break;
        }
    }
}

static void oled_clear(u8 light)
{
    u8 i;
    u8 buf[256] = {light};
    u8 len = 128;

    for(i = 0; i < 8; i ++)
    {
        oled_setCursor(i, 0); 
        iic_regs_write(&oleddev, 0x40, &buf[1], len);
    }
}

static int oled_init(void)
{
    /* 配置 oled */
    oled_writeCommand(0xAE);

    oled_writeCommand(0xD5);
    oled_writeCommand(0x80);

    oled_writeCommand(0xA8);
    oled_writeCommand(0x3F);

    oled_writeCommand(0xD3);
    oled_writeCommand(0x00);

    oled_writeCommand(0x40);

    oled_writeCommand(0xA1);

    oled_writeCommand(0xC8);

    oled_writeCommand(0xDA);
    oled_writeCommand(0x12);

    oled_writeCommand(0x81);
    oled_writeCommand(0xCF);

    oled_writeCommand(0xD9);
    oled_writeCommand(0xF1);

    oled_writeCommand(0xDB);
    oled_writeCommand(0x30);

    oled_writeCommand(0xA4);

    oled_writeCommand(0xA6);

    oled_writeCommand(0x8D);
    oled_writeCommand(0x14);

    oled_writeCommand(0xAF);
    mdelay(100);

    oled_clear(0xFF);
    oled_clear(0x0);

    /* 初始化显示内容 */
    oled_writeChar('o');
    oled_writeChar('l');
    oled_writeChar('e');
    oled_writeChar('d');
    oled_writeChar('0');
    oled_writeChar('.');
    oled_writeChar('9');
    oled_writeChar('6');

    return 0;
}

/* 字符设备读写，用于上层调用 */
static int oled_open(struct inode *inode, struct file *filp)
{
#ifdef ATOMIC
    if (!atomic_dec_and_test(&oleddev.lock))
    {
        atomic_inc(&oleddev.lock);
        return -EBUSY;
    }
#endif

#ifdef TIMER
    oleddev.period = 1000;
#endif

    filp->private_data = &oleddev; /* 设置私有数据 */
    oled_init();
    return 0;
}

static ssize_t oled_write(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    /* 第一次写字母需要清屏 */
    if (firstWrite == 1 || *buf == 0)
    {
        oled_clear(0xFF);
        oled_clear(0x0);
        firstWrite = 0;
        page = 1;
        column = 10;

        if (*buf == 0)
        {
            return 0;
        }
    }

    if (*buf == 1)
    {
        page += 2;
        page %= 6;
        column = 10;
        return 0;
    }

    oled_writeChar(*buf);
    return 0;
}

static int oled_release(struct inode *inode, struct file *filp)
{
    page = 1;
    column = 10;
    firstWrite = 1;
    /*struct iic_dev *dev = filp->private_data;*/
#ifdef ATOMIC
    atomic_inc(&dev->lock);
#endif

    return 0;
}

static struct file_operations oleddev_fops = {
    .owner = THIS_MODULE,
    .open = oled_open,
    .write = oled_write,
    .release = oled_release,
};

/* i2c设备的具体功能 */
static int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk(KERN_EMERG "\t match successed, initial iic charactor device\r\n");

    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (oleddev.major)
    {
        oleddev.devid = MKDEV(oleddev.major, 0);
        register_chrdev_region(oleddev.devid, DEV_CNT, CHRDEV_NAME);
    }
    else
    {
        alloc_chrdev_region(&oleddev.devid, 0, DEV_CNT, CHRDEV_NAME); /* 申请设备号 */
        oleddev.major = MAJOR(oleddev.devid); /* 获取主设备号 */
        oleddev.minor = MINOR(oleddev.devid); /* 获取次设备号 */
    }

    printk("oleddev major=%d,minor=%d\r\n",oleddev.major, oleddev.minor);

    /* 2、初始化 cdev */
    oleddev.cdev.owner = THIS_MODULE;
    cdev_init(&oleddev.cdev, &oleddev_fops);

    /* 3、添加一个 cdev */
    cdev_add(&oleddev.cdev, oleddev.devid, DEV_CNT);

    /* 4、创建类 */
    oleddev.class = class_create(THIS_MODULE, CHRDEV_NAME);
    if (IS_ERR(oleddev.class))
    {
        return PTR_ERR(oleddev.class);
    }

    /* 5、创建设备 */
    oleddev.device = device_create(oleddev.class, NULL, oleddev.devid, NULL, CHRDEV_NAME);
    if (IS_ERR(oleddev.device))
    {
        return PTR_ERR(oleddev.device);
    }

    oleddev.private_data = client;

#ifdef TIMER
    /* 初始化定时器 */
    init_timer(&oleddev.timer);
    oleddev.timer.function = timer_handle;
#endif

    return 0;
}

static int oled_remove(struct platform_device *dev)
{
#ifdef TIMER
    /* 删除定时器 */
    del_timer_sync(&oleddev.timer);
#endif

    /* 注销字符设备 */
    cdev_del(&oleddev.cdev);/* 删除 cdev */
    unregister_chrdev_region(oleddev.devid, DEV_CNT);

    device_destroy(oleddev.class, oleddev.devid);
    class_destroy(oleddev.class);

    return 0;
}

static int __init iic_oled_init(void)
{
    int ret;
    printk("oled drivers init\r\n");

    ret = i2c_add_driver(&oled_driver);

    return ret;
}

static void __exit iic_oled_exit(void)
{
    printk("oled drivers exit\r\n");

    i2c_del_driver(&oled_driver);
}

module_init(iic_oled_init);
module_exit(iic_oled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xue");


